//ModelSwarm.java
//The top-level ModelSwarm

import swarm.Globals;
import swarm.Selector;
import swarm.defobj.Zone;

import swarm.objectbase.Swarm;
import swarm.objectbase.SwarmImpl;
import swarm.objectbase.EmptyProbeMap;
import swarm.objectbase.EmptyProbeMapImpl;

import swarm.activity.ActionGroupImpl;
import swarm.activity.ScheduleImpl;
import swarm.activity.Activity;

import swarm.space.Grid2dImpl;
import swarm.collections.ListImpl;

public class ModelSwarm extends SwarmImpl 
{
	public int simulatedTime = 0;
	public int countryXSize = 15, countryYSize = 21;
	public Country country;
	public Market market;
	public Government gov;
	public ListImpl personList;
	public ScheduleImpl modelSchedule;
	public EmptyProbeMapImpl probeMap;
	// This is the constructor for a new ModelSwarm. All we do is to
	// use the constructor for ModelSwarm's parent class.
	public ModelSwarm(Zone azone) 
	{
		// Use the parent class to create a top-level swarm.
		super(azone);
		
		// Create the probe map and give it the ModelSwarm class
		probeMap = new EmptyProbeMapImpl(azone, getClass());
		
		// Now add probes for the variables we wish to probe.
		probeMap.addProbe(getProbeForVariable("countryXSize"));
		probeMap.addProbe(getProbeForVariable("countryYSize"));
		probeMap.addProbe(getProbeForVariable("simulatedTime"));
		Globals.env.probeLibrary.setProbeMap$For(probeMap, getClass());
	}

	// This is the method for building the model's objects: the food
	// space, the two-dimensional positioning grid, and the host of
	// bugs.
	public Object buildObjects() 
	{
		// use the parent class buildObject() method to initialize the
		// process
		super.buildObjects();

		// Now create the model's objects.
		country = new Country(Globals.env.globalZone, countryXSize, countryYSize);
		country.fastFillWithObject(null);
		market = new Market(country);
		gov = new Government(country, market);
		market.setGov(gov);
		
		// Now create a List object to manage all persons we are
		// about to create.
		personList = new ListImpl(Globals.env.globalZone);

		// Iterate over the grid with a certain probability of
		// creating a bug at each site.  If a bug is created, put it
		// on the grid and add it to the end of the person list.  Note
		// that we increment the bug number, num, each time a bug is
		// created.
		for (int x = 1; x < countryXSize; x++)
			for (int y = 1; y < countryYSize; y++)
			{
				if(x == 5 || x == 10) break; // Do not use the first column
			
				int preference = 0;
				
				if(x <= 4) {
					preference = AgentConstants.AGGRESSIVE;
				} else if(x > 5 && x < 10) {
					preference = AgentConstants.INDIFFERENCE;
				} else {
					preference = AgentConstants.CONSERVATIVE;
				}
				
				int chara = 15;//Globals.env.uniformIntRand.getIntegerWithMin$withMax(10, 20);
				
				Person p = new Person(Globals.env.globalZone, country, market, gov, 
						x, y, chara, preference);
				country.putObject$atX$Y(p, x, y);
				personList.addLast(p);
			}
		country.personList = this.personList;
		
		return this;
	}

	// This is the method a) for building the list of actions for
	// these objects to accomplish and b) for scheduling these actions
	// in simulated time.
	public Object buildActions() {
		Selector sel;
		ActionGroupImpl modelActions;

		// First, use the parent class to initialize the process.
		super.buildActions();

		// Then create an ActionGroup object and tell it to send a
		// randomWalk message to every bug in the bug list. Note the
		// change here from using the method createActionTo$message()
		// to send a message to a single object, to using the method
		// createActionForEach$message() to send the same message to
		// all the objects in a list.
		modelActions = new ActionGroupImpl(getZone());

		sel = SwarmUtils.getSelector(this, "removeBankruptPerson");
		modelActions.createActionTo$message(this, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "produce");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "consume");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Government()), "calculateGovPurchaseGrain");
		modelActions.createActionTo$message(gov, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "purchasedByGov");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "planNextYear");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "makeGrainDealPrice");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Market()), "grainDeal");
		modelActions.createActionTo$message(market, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "fixMoneyShortageOrBankrupt");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "improveLife");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "buyLand");
		modelActions.createActionForEach$message(this.personList, sel);
		
//		sel = SwarmUtils.getSelector((new Person()), "govHelp");
//		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Person()), "changeColor");
		modelActions.createActionForEach$message(this.personList, sel);
		
		sel = SwarmUtils.getSelector((new Government()), "adjustLandPrice");
		modelActions.createActionTo$message(gov, sel);

		sel = SwarmUtils.getSelector(this, "setSimulatedTime");
		modelActions.createActionTo$message(this, sel);
		

		// Now create the schedule and set the repeat interval to unity.
		modelSchedule = new ScheduleImpl(getZone(), 1);

		// Finally, insert the action list into the schedule at period zero
		modelSchedule.at$createAction(0, modelActions);

		return this;
	}

	// This method specifies the context in which the model is to be run.
	public Activity activateIn(Swarm swarmContext) {
		// Use the parent class to activate ourselves in the context
		// passed to us.
		super.activateIn(swarmContext);

		// Then activate the schedule in ourselves.
		modelSchedule.activateIn(this);

		// Finally, return the activity we have built.
		return getActivity();
	}

	//Remove the bankrupt person from the personList
	public void removeBankruptPerson()
	{
		for(int i = 0; i < this.personList.getCount(); i++)
		{
			Person p = (Person)personList.atOffset(i);
			if(p.getIfBankrupt() == true)
			{
				this.personList.remove(p);
				--i;
			}
		}
	}
	
	public void setSimulatedTime()
	{
		this.simulatedTime = Globals.env.getCurrentTime();
	}
	
	// The next three methods return some useful information about the
	// created ModelSwarm to the caller.  They will be used by the
	// Observer Swarm.
	public ListImpl getPersonList() {
		return personList;
	}

	public Grid2dImpl getWorld() {
		return country;
	}
	
	public Government getGov()
	{
		return this.gov;
	}
}
